// 这个js模块是关于axios的模块文件,关于axios的配置都会在这个js文件书写
import axios from 'axios'
//  创建axios实例的方式和之前全局axios.defaults.xxx这种配置差别不大
// axios.defaults.xxx
import store from '@/store'
// 导入json-bigint包 是来处理大数字的第三方包 使用方式和JSON.parse/JSON.stringify()
import JSONBig from 'json-bigint'
// 导入路由
import router from '@/router'
// 导入刷新token的接口
import { reqGetNewToken } from '@/api/user'
// 创建axios实力,对axios进行配置
const http = axios.create({
  // baseURL: 'http://toutiao.cn.utools.club', // 新地址 基地址或者本地服务器
  baseURL: 'http://toutiao-app.itheima.net/', // 旧备用地址
  /* ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  旧地址和新地址唯一不一样的地方是获取新闻推荐列表的时候地址是/v1_1/articles, 而
  且必须携带参数with_top: 1(详情查看api/channel.js第22行)
  ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
  */
  timeout: 3000, // 请求服务3s没有响应,终止请求
  // 将服务器返回的数据在.then.catch之前对象返回的数据可以在这个配置节点修改处理响应数据
  transformResponse: [function(data) {
    // 对 data 进行任意转换处理
    // console.log(data) // json字符串 响应到的数据
    // trycatch的作用是如果返回的数据能转换成json对象就转,转不了就原样输出
    try {
      return JSONBig.parse(data)
    } catch {
      return data
    }
  }]
})
// 添加请求拦截器
// 只要使用axios向服务器发送的请求,都会经过请求拦截器
http.interceptors.request.use(function(config) {
  // console.log('经过了请求拦截器')
  console.log(config) // config就是当前请求的配置信息对象, 中有一个headers可以自定义请求头信息
  // config.headers.a = 10
  const token = store.state.user.tokenInfo.token
  // 如果有token 并且没有挂载过Authorization 再进入这个分支
  if (token && !config.headers.Authorization) {
    config.headers.Authorization = `Bearer ${token}`
  }
  // 在发送请求之前做些什么
  return config
}, function(error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})
// 添加响应拦截器 服务器返回给客户端的数据在.then和.catch之前可以在响应拦截器中做一些事情
// 拦截器中正确的响应会经过函数一,失败的响应会经过函数2
http.interceptors.response.use(function(response) {
  // 对响应数据做点什么
  // console.dir(response)
  return response
}, async function(error) {
  // 只要响应失败报错,都会进入这个函数是
  // 对响应错误做点什么
  console.dir(error) // error错误对象中有一个response是一个响应对象, 这个对象中有一个status状态码 401
  // console.log('响应拦截器拦截错误的msg')
  if (error.response.status === 401) { // token失效了,只要能进入这个分支,在这个分支中做token刷新的操作
    // console.log('token失效了')
    // 先判断是否有refresh_token, 有 刷新新的token,没有去重新登录
    const refresh_token = store.state.user.tokenInfo.refresh_token
    if (refresh_token) {
      // 使用try catch 处理 refresh_token也过期的情况, refresh_token过期了 只能让用户重新登录
      try {
        // 刷新新token
        console.log('刷新token, 发送请求 得到新的token')
        const { data: { data: { token }}} = await reqGetNewToken(refresh_token)
        console.log(token)
        // 更新vuex和本地存储
        store.commit('user/setTokenInfo', {
          token,
          refresh_token
        })
        // 重新发送之前失败的请求
        // 将上一次失败的请求中的token修改成最新拿到的token  这句话一定要写, 一直携带过期的token 一直发送请求
        error.config.headers.Authorization = `Bearer ${token}`
        // error对象中的config就是上一次请求的配置信息
        return http(error.config)
      // http({
      //   method: 'get',
      //   url: 'xxx',
      //   data:/ params: {}
      // })
      } catch {
        store.commit('user/removeTokenInfo')
        router.push({
          path: '/login',
          query: {
            // router.currentRoute 就是当前路由信息对象 和 导航守卫中to 表示的是一个意思
            backto: router.currentRoute.fullPath // 携带之前要去的路由地址的参数
          }
        })
      }
    } else {
      // 重新登录
      // console.log(router.currentRoute)
      // 优化,如果没有refresh_token, 重新登录, 有必要将本地存储的token信息和vuex中的token信息清除
      store.commit('user/removeTokenInfo')
      router.push({
        path: '/login',
        query: {
          // router.currentRoute 就是当前路由信息对象 和 导航守卫中to 表示的是一个意思
          backto: router.currentRoute.fullPath // 携带之前要去的路由地址的参数
        }
      })
    }
  }
  return Promise.reject(error)
})
export default http
